﻿using System;
using System.Linq;
using RayDen.Library.Core;
using RayDen.Library.Core.Components;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Frames;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;


namespace RayDen.RayEngine.Data
{
    public class PointLight : ILight
    {
        public Point Position;
        public RgbSpectrum Power;
        public float FalloffStart, FalloffEnd;

        public SampledSpectrum Spectra;
        public EmissionProfile Profile { get; set; }

        public string Name
        {
            get { return "PointLight" + Guid.NewGuid(); }
        }
        public PointLight() { }
        public PointLight(FrameLightsource frameLight)
        {
            this.Position = (Point)frameLight.Parameters.Get<Vector>(FrameLightsource.PointLightPosition);
            this.Power = frameLight.Parameters.Get<RgbSpectrum>(FrameLightsource.LightsourcePower);
            this.Spectra = SPD_Data.FromSampled(SPD_Data.D5000, SpectrumType.Illuminant);
            //new SampledSpectrum(ref this.Power, SpectrumType.Illuminant);
        }
        public PointLight(Point pos, RgbSpectrum power, SampledSpectrum spd)
        {
            this.Position = pos;
            this.Power = power;
            this.Spectra = SPD_Data.FromSampled(SPD_Data.D5000, SpectrumType.Illuminant);
            //lightSpectra = GlobalConfiguration.Instance.SpectralRendering ? Spectra.ToArray() : this.Power.ToArray();
        }
        public RgbSpectrumInfo Emittance(ref Vector dir, out float pdf)
        {
            pdf = MC.UniformSpherePdf();
            return new RgbSpectrumInfo((Vector)(this.Power / (this.Position - dir).Length2()));
        }

        #region ILightsource Members
        public bool IsDelta
        {
            get
            {
                return true;
            }
        }

        public ISpectrum Le(ref Vector dir)
        {
            return (Power / (Position - dir).Length2());
        }

        public void EvaluateShadow(ref Point point, ref Normal n, float u0, float u1, float u2, ref LightSample result)
        {
            if (result == null)
                result = new LightSample();
            var dir = -(Position - point);
            var l2 = dir.Length2();
            var l = MathLab.Sqrt(l2);
            dir.Normalize();
            result.Pdf = MC.UniformSpherePdf();
            result.LightRay = new RayData(ref point, ref dir, 1e-4f, l - 1e-4f);
            result.Distance = l;
            float theta = Vector.SphericalTheta(ref dir);
            var i = Profile.Evaluate(Vector.SphericalPhi(ref dir) * MathLab.INVTWOPI, theta * MathLab.INVPI);
            i.Div(l);
            var p = (ISpectrum) Power;
            i.Mul(ref p);
            result.Spectrum = i;
        }

        public void EvaluatePhoton(IRayEngineScene scene, float u0, float u1, float u2, float u3, float u4, out LightSample result)
        {
            result = new LightSample();

            Vector dir = MC.UniformSampleSphere(u0, u1);

            result.LightRay = new RayData(ref Position, ref dir, 1e-4f, 1e4f);

            result.Pdf = MathLab.INV4PI;

            result.Spectrum =new RgbSpectrumInfo((Power * MathLab.M_PI * 4f));
        }



        #endregion


        public void Le(ref Vector dir, ref ISpectrum le)
        {
            ISpectrum ls = (Power / (Position - dir).Length2());
            le.Mul(ref ls);
        }


        public void SampleSpatialDirect(ref Point point, ref Normal n, float u0, float u1, float u2, ref LightSample ls)
        {
            var dir = -(Position - point);
            var l2 = dir.Length2();
            var l = MathLab.Sqrt(l2);
            dir.Normalize();
            ls.Pdf = MC.UniformSpherePdf();
            ls.Distance = l;
            float theta = Vector.SphericalTheta(ref dir);
            var i = Profile.Evaluate(Vector.SphericalPhi(ref dir) * MathLab.INVTWOPI, theta * MathLab.INVPI);
            i.Div(l);
        }

        public void SampleSpatialAdjoint(IRayEngineScene scene, float u0, float u1, float u2, float u3, float u4, out Vector dir, out float distance, out float pdf)
        {
            dir = MC.UniformSampleSphere(u0, u1);
            distance = 1.0f;
            pdf = MathLab.INV4PI;
        }
    }
}